/**
 @file ctc_tma_interrupt.c

 @author  Copyright (C) 2020 Centec Networks Inc.  All rights reserved.

 @date 2020-06-20

 @version v2.0

 The file contains all interrupt APIs
*/

/****************************************************************************
*
* Header Files
*
****************************************************************************/
#include "ctc_asw_common.h"
#include "ctc_asw_interrupt.h"
#include "asw/include/drv_api.h"
#include "dal.h"
/****************************************************************************
*
* Defines and Macros
*
*****************************************************************************/
#define CTC_ASW_INTR_INDEX_VAL_SET          0
#define CTC_ASW_INTR_INDEX_VAL_RESET      1
#define CTC_ASW_INTR_INDEX_MASK_SET       2
#define CTC_ASW_INTR_INDEX_MASK_RESET   3

#define CTC_ASW_INTR_SUP_STATUS_WORD   2
#define CTC_ASW_INTR_SUP_SWITCH_CORE_IDX 19
#define CTC_ASW_INTR_SUP_PCS_LINK_IDX 23
#define CTC_ASW_INTR_SUP_INT_PHY0 24
#define CTC_ASW_INTR_SUP_INT_PHY1 25
#define CTC_ASW_INTR_INT_PHY0_NUM 4
#define CTC_ASW_INTR_SUP_LINK_START 39
#define CTC_ASW_INTR_SUP_LINK_END  57
/****************************************************************************
*
* Global and Declaration
*
*****************************************************************************/
#ifndef MEM_MODEL
extern int32
drv_chip_read(uint8 lchip, addrs_t offset, uint32* p_value, uint32 len);
extern int32
drv_chip_write(uint8 lchip, addrs_t offset, uint32* p_value, uint32 len);
#endif
extern void
_ctc_asw_chip_phy_link_event_polling_cb(uint8 lchip, uint8 start_idx, uint8 end_idx);
ctc_asw_intr_info_t intr_info[CTC_MAX_LOCAL_CHIP_NUM]={0};
extern dal_op_t g_dal_op;
/****************************************************************************
*
*  Functions
*
*****************************************************************************/
int32
_ctc_asw_intr_register_cb_func(uint8 lchip, ctc_asw_interrupt_cb_t intr_cb, CTC_INTERRUPT_FUNC isr)
{
    CTC_MAX_VALUE_CHECK(intr_cb, CTC_ASW_CB_MAX-1);
    intr_info[lchip].isr[intr_cb] = isr;
    return CTC_E_NONE;
}

int32
_ctc_asw_intr_get_event_func(uint8 lchip, ctc_interrupt_event_t intr_cb, CTC_INTERRUPT_EVENT_FUNC* isr)
{
    CTC_MAX_VALUE_CHECK(intr_cb, CTC_EVENT_MAX-1);
    *isr = intr_info[lchip].event_isr[intr_cb];
    return CTC_E_NONE;
}

static void
_ctc_asw_intr_thread(void* param)
{
    uint32 irq = *((uint32*)param);
    uint8 lchip = 0;
    uint32 cmd_st = 0;
    uint32 cmd_mask = 0;
    uint32 cmd_sup = 0;
    uint32 cmd_sup_mask = 0;
    uint32 func_val[CTC_ASW_INTR_SUP_STATUS_WORD] = {0};
    uint32 func_mask[CTC_ASW_INTR_SUP_STATUS_WORD] = {0};
    uint8 index = 0;
    uint32 sup_intr_val[CTC_ASW_INTR_SUP_STATUS_WORD] = {0};
    uint32 sup_intr_mask[CTC_ASW_INTR_SUP_STATUS_WORD] = {0};

    /*read  suppadintr value*/
    cmd_sup = DRV_IOR(SupPadIntr_t, DRV_ENTRY_FLAG);
    DRV_IOCTL(lchip, CTC_ASW_INTR_INDEX_VAL_SET, cmd_sup, &sup_intr_val);

    /*read  suppadintr mask*/
    cmd_sup_mask = DRV_IOR(SupPadIntr_t, DRV_ENTRY_FLAG);
    DRV_IOCTL(lchip, CTC_ASW_INTR_INDEX_MASK_SET, cmd_sup_mask, &sup_intr_mask);

    for(index = 0;index < CTC_ASW_INTR_SUP_STATUS_WORD; index++)
    {
        sup_intr_val[index] = sup_intr_val[index] & (~sup_intr_mask[index]);
    }
    
    if ((0 == sup_intr_val[0]) && (0 == sup_intr_val[1]))
    {
        return;
    }

    /*write  suppadintr mask*/
    cmd_sup = DRV_IOW(SupPadIntr_t, DRV_ENTRY_FLAG);
    DRV_IOCTL(lchip, CTC_ASW_INTR_INDEX_MASK_SET, cmd_sup, &sup_intr_val);

    if (CTC_IS_BIT_SET(sup_intr_val[CTC_ASW_INTR_SUP_SWITCH_CORE_IDX>>5], CTC_ASW_INTR_SUP_SWITCH_CORE_IDX%32))
    {
        cmd_st = DRV_IOR(GlobalIntStatusSet_t, DRV_ENTRY_FLAG);
        cmd_mask = DRV_IOR(GlobalIntMaskSet_t, DRV_ENTRY_FLAG);

        DRV_IOCTL(lchip, 0, cmd_st, &func_val);
        DRV_IOCTL(lchip, 0, cmd_mask, &func_mask);

        func_val[0] = func_val[0] & (~func_mask[0]);
        cmd_st = DRV_IOW(GlobalIntStatusReset_t, DRV_ENTRY_FLAG);
        DRV_IOCTL(lchip, 0, cmd_st, &func_val);
        cmd_st = DRV_IOW(GlobalIntMaskReset_t, DRV_ENTRY_FLAG);
        DRV_IOCTL(lchip, 0, cmd_st, &func_mask);

        if (GetGlobalIntStatusSet(V,learnFifoInt_f, &func_val))
        {
            intr_info[lchip].isr[CTC_ASW_CB_L2_SW_LEARNING](lchip, 0, NULL);
        }

        if (GetGlobalIntStatusSet(V,moveFifoInt_f, &func_val))
        {
            intr_info[lchip].isr[CTC_ASW_CB_L2_STATION_MOVE](lchip, 0, NULL);
        }

        if (GetGlobalIntStatusSet(V,dmaCompInt_f, &func_val))
        {
            intr_info[lchip].isr[CTC_ASW_CB_DMA](lchip, 0, NULL);
        }

        if (GetGlobalIntStatusSet(V,eccErrInt_f, &func_val))
        {
            intr_info[lchip].isr[CTC_ASW_CB_ECC_ERROR](lchip, 0, NULL);
        }
    }
    if (CTC_IS_BIT_SET(sup_intr_val[CTC_ASW_INTR_SUP_PCS_LINK_IDX>>5], CTC_ASW_INTR_SUP_PCS_LINK_IDX%32))
    {
        cmd_st = DRV_IOR(SupPcsFuncIntr_t, DRV_ENTRY_FLAG);
        DRV_IOCTL(lchip, CTC_ASW_INTR_INDEX_VAL_SET, cmd_st, &func_val);
        DRV_IOCTL(lchip, CTC_ASW_INTR_INDEX_MASK_SET, cmd_st, &func_mask);
        for(index = 0;index < CTC_ASW_INTR_SUP_STATUS_WORD; index++)
        {
            func_val[index] = func_val[index] & (~func_mask[index]);
        }
        if ((0 != func_val[0]) || (0 != func_val[1]))
        {

            intr_info[lchip].isr[CTC_ASW_CB_PCS_LINK](lchip, 0, func_val);

            cmd_st = DRV_IOW(SupPcsFuncIntr_t, DRV_ENTRY_FLAG);
            DRV_IOCTL(lchip, CTC_ASW_INTR_INDEX_VAL_RESET, cmd_st, &func_val);
            DRV_IOCTL(lchip, CTC_ASW_INTR_INDEX_MASK_RESET, cmd_st, &func_mask);
        }
    }
    if (CTC_IS_BIT_SET(sup_intr_val[CTC_ASW_INTR_SUP_INT_PHY0>>5], CTC_ASW_INTR_SUP_INT_PHY0%32))
    {
        _ctc_asw_chip_phy_link_event_polling_cb(lchip, 0, CTC_ASW_INTR_INT_PHY0_NUM);
    }
    if (CTC_IS_BIT_SET(sup_intr_val[CTC_ASW_INTR_SUP_INT_PHY1>>5], CTC_ASW_INTR_SUP_INT_PHY1%32))
    {
        _ctc_asw_chip_phy_link_event_polling_cb(lchip, CTC_ASW_INTR_INT_PHY0_NUM, CTC_ASW_INTERNAL_PHY_PORT_NUM);
    }

    if (g_dal_op.interrupt_set_en)
    {
        g_dal_op.interrupt_set_en(irq, TRUE);
    }

    /*release value and  mask suppadintr*/
    cmd_sup = DRV_IOW(SupPadIntr_t, DRV_ENTRY_FLAG);
    DRV_IOCTL(lchip, CTC_ASW_INTR_INDEX_VAL_RESET, cmd_sup, &sup_intr_val);

    /*write  suppadintr mask*/
    cmd_sup = DRV_IOW(SupPadIntr_t, DRV_ENTRY_FLAG);
    DRV_IOCTL(lchip, CTC_ASW_INTR_INDEX_MASK_RESET, cmd_sup, &sup_intr_val);
}

int32
ctc_asw_interrupt_init(uint8 lchip, void* p_global_cfg)
{
    int32 ret = CTC_E_NONE;
    GlobalIntEn_m glb_int;
    GlobalIntMaskReset_m glb_int_mask;
    GlobalIntStatusSet_m glb_int_status;
    ds_t ds;
    uint32 cmd = 0;
    uint8 idx = 0;
    ctc_intr_global_cfg_t* p_global_cfg_param = (ctc_intr_global_cfg_t*)p_global_cfg;
    CTC_PTR_VALID_CHECK(p_global_cfg);
    if (p_global_cfg_param->group_count > CTC_INTR_MAX_GROUP)
    {
        return CTC_E_INVALID_PARAM;
    }

    sal_memset(&glb_int, 0, sizeof(glb_int));
    sal_memset(&glb_int_mask, 0, sizeof(glb_int_mask));
    sal_memset(&glb_int_status, 0, sizeof(glb_int_status));
    sal_memset(&ds, 0, sizeof(ds));

    cmd = DRV_IOW(GlobalIntEn_t, DRV_ENTRY_FLAG);
    SetGlobalIntEn(V,gblInt_f, &glb_int, 1);
    ret = DRV_IOCTL(lchip, 0, cmd, &glb_int);
    SetGlobalIntMaskReset(V,dmaCompInt_f, &glb_int_mask, 0xf);
    SetGlobalIntMaskReset(V,halfDuplexInt_f, &glb_int_mask, 1);
    SetGlobalIntMaskReset(V,moveFifoInt_f, &glb_int_mask, 1);
    SetGlobalIntMaskReset(V,eccErrInt_f, &glb_int_mask, 1);
    SetGlobalIntMaskReset(V,learnFifoInt_f, &glb_int_mask, 1);
    SetGlobalIntMaskReset(V,gblInt_f, &glb_int_mask, 1);
    cmd = DRV_IOW(GlobalIntMaskReset_t, DRV_ENTRY_FLAG);
    ret = ret?ret:DRV_IOCTL(lchip, 0, cmd, &glb_int_mask);
    cmd = DRV_IOR(GlobalIntStatusSet_t, DRV_ENTRY_FLAG);
    ret = ret?ret:DRV_IOCTL(lchip, 0, cmd, &glb_int_status);
    cmd = DRV_IOW(GlobalIntStatusReset_t, DRV_ENTRY_FLAG);
    ret = ret?ret:DRV_IOCTL(lchip, 0, cmd, &glb_int_status);
    sal_memset(&ds, 0xFF, sizeof(ds));
    /*init release mask suppadintr*/
    cmd = DRV_IOW(SupPadIntr_t, DRV_ENTRY_FLAG);
    ret = ret?ret:DRV_IOCTL(lchip, CTC_ASW_INTR_INDEX_VAL_RESET, cmd, &ds);
    /*write  suppadintr mask*/
    cmd = DRV_IOW(SupPadIntr_t, DRV_ENTRY_FLAG);
    ret = ret?ret:DRV_IOCTL(lchip, CTC_ASW_INTR_INDEX_MASK_RESET, cmd, &ds);

    cmd = DRV_IOW(SupPcsFuncIntr_t, DRV_ENTRY_FLAG);
    ret = ret?ret:DRV_IOCTL(lchip, CTC_ASW_INTR_INDEX_VAL_RESET, cmd, &ds);
    cmd = DRV_IOW(SupPcsFuncIntr_t, DRV_ENTRY_FLAG);
    ret = ret?ret:DRV_IOCTL(lchip, CTC_ASW_INTR_INDEX_MASK_RESET, cmd, &ds);
    for (idx = 0; idx < CTC_INTR_MAX_GROUP; idx++)
    {
        if (idx < p_global_cfg_param->group_count)
        {
            sal_memcpy(&intr_info[lchip].intr_group, &p_global_cfg_param->p_group[idx], sizeof(ctc_intr_group_t));
        }
        else
        {
            intr_info[lchip].intr_group[idx].group = -1;
        }
    }
    if((p_global_cfg_param->intr_mode > 0) && g_dal_op.interrupt_get_irq)
    {
        uint16 irqs[8] = {0};
        uint8  irq_num = 0;

        ret = g_dal_op.interrupt_get_irq(lchip, p_global_cfg_param->intr_mode, irqs, &irq_num);
        if (ret)
        {
            return ret;
        }

        /* update per group's irq */
        for (idx = 0; idx < CTC_INTR_MAX_GROUP; idx++)
        {
            intr_info[lchip].intr_group[idx].irq = irqs[idx];
        }
    }

    /* 8. call DAL function to register IRQ */
    if (g_dal_op.interrupt_register)
    {
        for (idx = 0; idx < p_global_cfg_param->group_count; idx++)
        {
            ret = g_dal_op.interrupt_register(intr_info[lchip].intr_group[idx].irq, intr_info[lchip].intr_group[idx].prio, _ctc_asw_intr_thread, (void*)(uintptr)(idx));
            if (ret < 0)
            {
                return CTC_E_NO_RESOURCE;
            }
            if (g_dal_op.interrupt_set_en)
            {
                g_dal_op.interrupt_set_en(intr_info[lchip].intr_group[idx].irq, FALSE);
                g_dal_op.interrupt_set_en(intr_info[lchip].intr_group[idx].irq, TRUE);
            }
        }
    }

    return ret;
}


int32
ctc_asw_interrupt_deinit(uint8 lchip)
{
    uint32 i = 0;
    ctc_intr_group_t* p_group = NULL;
    int32 ret = 0;

    /* free interrupt */
    if (g_dal_op.interrupt_unregister)
    {
        for (i = 0; i < CTC_INTR_MAX_GROUP; i++)
        {
            p_group = &intr_info[lchip].intr_group[i];
            if (p_group->group < 0)
            {
                continue;
            }
            ret = g_dal_op.interrupt_unregister(p_group->irq);
            if (ret < 0)
            {
                return ret;
            }
        }
    }

    return ret;
}


int32
ctc_asw_interrupt_register_event_cb(uint8 lchip, ctc_interrupt_event_t event, CTC_INTERRUPT_EVENT_FUNC cb)
{
    CTC_API_LOCK(lchip);

    switch (event)
    {
        case CTC_EVENT_L2_SW_LEARNING:
        case CTC_EVENT_PORT_LINK_CHANGE:
        case CTC_EVENT_ECC_ERROR:
            intr_info[lchip].event_isr[event] = cb;
            break;

        default:
            CTC_API_UNLOCK(lchip);
            return CTC_E_INVALID_PARAM;
    }

    CTC_API_UNLOCK(lchip);

    return CTC_E_NONE;
}

